package sliceybxb

import (
	"fmt"
)

/**
 * @ProjectName: go-test
 * @PackageName: _3_slice
 * @Description:
 * @Author: hy.bai
 * @Date: 2025-07-21 13:54
 */
func TestSlice() {
	// 切片的定义：var 变量名 []type，slice是引用数据类型，底层是一个数组
	var ints = []int{}
	// 注意:定义切片时，建议提前估算好,尽量避免切片自动扩容。
	// var ints = make([]int, 10)
	fmt.Println("切片ints的长度是：", len(ints))
}

/*
 * @Description: 测试切片的扩容
 */
func TestSliceAppend() {
	// 切片的定义：var 变量名 []type，slice是引用数据类型，底层是一个数组
	var ints = []int{1}
	// 切片的扩容，第二个参数是添加到切片中的元素
	var resultIntS = append(ints, 2)
	fmt.Println("切片ints的长度是：", len(ints))
	fmt.Println("切片ints的长度是：", len(resultIntS))
	// 添加多个元素
	result1 := append(ints, 3, 4)
	result2 := append(ints, []int{3, 4}...) // 第二个参数是相同类型的切片。
	fmt.Println("切片result1的长度是：", result1)
	fmt.Println("切片result2的长度是：", result2)
}

func TestAppendInt() {
	// append()添加元素和切片扩容
	var numSlice = []int{} // 长度为0
	var numSlice1 []int    // 为初始化，此时切片为nil
	// 注意:通过var声明的切片可以在append()函数直接使用，无需初始化。
	for i := 1; i <= 10; i++ {
		numSlice = append(numSlice, i)
		numSlice1 = append(numSlice1, i) // append会自动初始化切片。原因是：append函数可以实现自动扩容。
		fmt.Printf("numSlice值是%v 长度是:%d 容量是:%d 指针地址:%p\n", numSlice, len(numSlice), cap(numSlice), numSlice)
		fmt.Printf("numSlice1值是%v 长度是:%d 容量是:%d 指针地址:%p\n", numSlice1, len(numSlice1), cap(numSlice1), numSlice1)
	}
}

/**
 * @Description: 添加多个元素
 */
func TestAppend() {

	var citySlice []string
	citySlice = append(citySlice, "北京")              //  添加一个元素
	citySlice = append(citySlice, "上海", "郑州", "驻马店") // 添加多个元素
	a := []string{"成都", "重庆"}
	citySlice = append(citySlice, a...) // 添加其他同类型的切片
	fmt.Println(citySlice)              // 打印结果：[北京 上海 郑州 驻马店 成都 重庆]
}

// 切片是引用数据类型，但也是值传递。赋值时，传递的是切片对应的数组的内存地址。
func TestSliceReference() {
	var tsr = []int{10, 20, 30}
	var tsr1 = tsr
	tsr1[1] = 22
	fmt.Println(tsr, tsr1) // [10 22 30] [10 22 30]
	// 切片之间赋值是复制数组的内存地址。
	// fmt.Println("tsr的内存地址:", &tsr)
	fmt.Printf("tsr的内存地址:%p\n", tsr)
	fmt.Printf("tsr1的内存地址:%p\n", tsr1)
	// 注意:打印出来的内存地址是：0xc0000aa000(tsr和tsr1是相同的)
}

func TestDSlice() {
	// 切片是引用类型，不支持直接比较，只能和nil比较
	// 切片声明方式1：不推荐
	var a []string        // 声明一个字符串切片，而且不会申请内存地址。
	fmt.Println(a)        // 打印结果：[]
	fmt.Println(a == nil) // true

	// 切片的声明方式2(推荐)
	var b = []int{}       // 声明一个整型切片并初始化，会申请内存地址。
	fmt.Println(b)        // []
	fmt.Println(b == nil) // false, 不是nil对象，而是空切片

	// 切片的声明方式3(推荐)
	var c = []bool{false, true} // 声明一个布尔类型的切片，并初始化
	fmt.Println(c)              // [false true]
	fmt.Println(c == nil)       // false

	// 切片的声明方式4
	var d = make([]int, 3)
	fmt.Println(d) // 打印结果:[0 0 0]
}

// 切片的本质：切片的底层是数组
func TestArraySlice() {
	var arr = [8]int{0, 1, 2, 3, 4, 5, 6, 7}
	fmt.Printf("变量arr的类型是%T, 值是%v\n", arr, arr)
	// 这里表示切片as1的底层数组是arr。
	var as1 = arr[:5] // 切片as1的类型是：[]int, 值是：[0 1 2 3 4], 长度是：5, 容量是8
	fmt.Printf("切片as1的类型是：%T, 值是：%v, 长度是：%d, 容量是%d\n", as1, as1, len(as1), cap(as1))
	var as2 = arr[3:5] // 切片as2的类型是：[]int, 值是：[3 4], 长度是：2, 容量是5
	fmt.Printf("切片as2的类型是：%T, 值是：%v, 长度是：%d, 容量是%d\n", as2, as2, len(as2), cap(as2))
	as3 := as2[:cap(as2)] // 切片as3的类型是：[]int, 值是：[3 4 5 6 7], 长度是：5, 容量是5
	fmt.Printf("切片as3的类型是：%T, 值是：%v, 长度是：%d, 容量是%d\n", as3, as3, len(as3), cap(as3))
	//
	// 注意：切片as1,as2,as3底层对应的数组都是arr
	fmt.Printf("as1的内存地址是：%p\n", as1)
	fmt.Printf("as2的内存地址是：%p\n", as2)
	fmt.Printf("as3的内存地址是：%p\n", as3)
}

/*
 * @Description: 删除切片元素
 */
func TestDelete() {
	var numSlice1 = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
	ints1 := numSlice1[:5]
	ints2 := numSlice1[6:]
	fmt.Printf("ints1的内存地址是：%p\n", ints1)
	fmt.Printf("ints2的内存地址是：%p\n", ints2)

	// 注意：ints1和ints2的内存地址都是0xc0000a40a0。所以ints1和ints2底层用的是同一个数组
	numSlice2 := append(ints1, ints2...) // 删除索引值为5的元素
	fmt.Println(numSlice1)               // 打印结果：[1 2 3 4 5 7 8 9 10 10]
	fmt.Println(numSlice2)               // 打印结果：[1 2 3 4 5 7 8 9 10]

	fmt.Printf("numSlice1的内存地址是：%p\n", numSlice1)
	fmt.Printf("numSlice2的内存地址是：%p\n", numSlice2)
	// 注意：numSlice1和numSlice2的内存地址是一样的，
	// 因为切片是引用数据类型，底层数组是同一个，所以numSlice1和numSlice2的内存地址
	// numSlice1打印结果是[1 2 3 4 5 7 8 9 10 10]原因是：
	// numSlice1和numSlice2的内存地址是一样的，所以执行的底层数值是同一个，而通过append方法进行删除元素时，会覆盖底层数组的元素，
	// 所以：numSlice1的索引值6~9的位置也会被修改。
}

// 合并切片
func TestMerge() {
	var nSlice1 = []int{1, 2, 3}
	var nSlice2 = []int{7, 8, 9}
	numSlice := append(nSlice1, nSlice2...) // 合并两个切片
	fmt.Println(numSlice)                   // 打印结果：[1 2 3 7 8 9]

	// 明白指针之后执行
	fmt.Printf("nSlice1的内存地址是：%p\n", nSlice1)   // 内存地址：0xc0000aa000
	fmt.Printf("nSlice2的内存地址是：%p\n", nSlice2)   // 内存地址：0xc0000aa018
	fmt.Printf("numSlice的内存地址是：%p\n", numSlice) // 内存地址：0xc0000ac000
	// 注意:生成的三个切片分别是不同的内存地址
}

// for循环遍历切片
func TestFor() {
	var sForData = []string{"北京", "上海", "广州"}
	for i := 0; i < len(sForData); i++ {
		fmt.Println(i, ":", sForData[i])
	}
}

// range循环遍历切片
func TestForEach() {
	var sForData = []string{"北京", "上海", "广州"}
	for i, elem := range sForData {
		fmt.Println(i, ":", elem)
	}
}
